#include <QDebug>
#include <QtMath>
#include "demuxthread.h"
#include "ffinclude.h"
#include "videoplayer.h"
#include "decodethread.h"
#include "audiodecodethread.h"
#include "textdecodethread.h"

DemuxThread::DemuxThread(VideoPlayer *player) : QThread (player)
{
    m_player=player;
    m_reqPause=false;
    m_reqStop=false;
    m_reqSeek=false;
    m_lastReqPause=false;
}

DemuxThread::~DemuxThread()
{
    this->stop();
}

void DemuxThread::play()
{
    if(!this->isRunning()){
        m_reqPause=false;
        m_lastReqPause=false;
        m_reqSeek=false;
        m_reqStop=false;
        this->start();
    }else{
        m_reqPause=false;
        m_reqStop=false;
        m_waitCond.wakeAll();
    }
}

void DemuxThread::pause()
{
    m_reqStop=false;
    m_reqPause=true;
}

void DemuxThread::stop()
{
    m_reqStop=true;
    m_reqPause=false;
    m_lastReqPause=false;
    m_reqSeek=false;
    m_waitCond.wakeAll();
    this->wait();
}

void DemuxThread::seek(int ms)
{
    m_seekPos=ms;
    m_reqSeek=true;
    m_waitCond.wakeAll();
}

void DemuxThread::run()
{
    m_lastReadDateTime=QDateTime();
    m_streamOpenDateTime=QDateTime();
    m_lastErrorMsg="";
    m_lastReqPause=false;
    m_startPosSeekDone=false;

    PlayerInfo *playerInfo = &m_player->m_playerInfo;
    MediaInfo* mediaInfo = &m_player->m_mediaInfo;
    PlayerConfig* playerConfig=&m_player->m_playerConfig;

    //打开码流
    if(!openStream()){
        emit error(m_lastErrorMsg);
        return ;
    }

    //打开缓冲
    startPacketQueue();

    emit inited();

    //启动解码
    if(playerInfo->videoStream){
        //首次启动时需要将封面帧压入队列，后面解包时不再压入
        enqueueVideoAttachFrame();
        m_player->m_vcodecThread->play();
    }
    if(playerInfo->audioStream){
        m_player->m_acodecThread->play();
    }
    if(playerInfo->textStream){
        m_player->m_textcodecThread->play();
    }

    //解复用
    int retCode=-1;
    AVPacket pkt1, *pkt = &pkt1;
    bool cond1=false,cond2=false;
    for (;;) {
        if (this->m_reqStop)
            break;

        //暂停拉流
        if (this->m_reqPause != this->m_lastReqPause) {
            this->m_lastReqPause = this->m_reqPause;
            if (this->m_reqPause){
                av_read_pause(playerInfo->formatContext);
            }else{
                av_read_play(playerInfo->formatContext);
            }
        }

        //定位
        if(!m_startPosSeekDone){
            m_startPosSeekDone=true;
            if(!playerInfo->isLiveStream && playerConfig->startPos>0 && playerConfig->startPos<mediaInfo->duration){
                int64_t timestamp = playerConfig->startPos*1000;
                if (playerInfo->formatContext ->start_time != AV_NOPTS_VALUE){
                    //从第一帧的位置开始偏移
                    timestamp += playerInfo->formatContext->start_time;
                }
                retCode=avformat_seek_file(playerInfo->formatContext, -1, INT64_MIN, timestamp, INT64_MAX, 0);
                if(retCode>=0){
                    emit seekDone(true,"");
                }else{
                    emit seekDone(false,avErrorCodeToString(retCode));
                }
            }
        }else if(m_reqSeek){
            m_reqSeek=false;
            if(!playerInfo->isLiveStream && this->m_seekPos>0 && this->m_seekPos<=mediaInfo->duration){
                int64_t timestamp = this->m_seekPos*1000;
                retCode = avformat_seek_file(playerInfo->formatContext, -1, INT64_MIN, timestamp, INT64_MAX, 0);
                if (retCode >= 0) {
                    if (playerInfo->audioStream) {
                        packet_queue_flush(&playerInfo->audioPacketQueue);
                        packet_queue_put(&playerInfo->audioPacketQueue, &playerInfo->flushPkt);
                    }
                    if (playerInfo->textStream ) {
                        packet_queue_flush(&playerInfo->textPacketQueue);
                        packet_queue_put(&playerInfo->textPacketQueue, &playerInfo->flushPkt);
                    }
                    if (playerInfo->videoStream ) {
                        packet_queue_flush(&playerInfo->videoPacketQueue);
                        packet_queue_put(&playerInfo->videoPacketQueue, &playerInfo->flushPkt);
                    }
                    if (/*is->seek_flags & AVSEEK_FLAG_BYTE*/false) {
                        set_clock(&playerInfo->externalClock, NAN, 0);
                    } else {
                        set_clock(&playerInfo->externalClock, timestamp/(double)AV_TIME_BASE, 0);
                    }

                    //执行完seek后，由于封面帧缓存已经清除，因此需要重新缓存它
                    playerInfo->demuxEof = false;
                    if(playerInfo->videoStream){
                        enqueueVideoAttachFrame();
                    }

                    emit seekDone(true,"");
                }else{
                    emit seekDone(false,avErrorCodeToString(retCode));
                }
            }
        }

        //非直播情况下，如果缓冲区已满则暂停解包，则等待解码线程消费
        if(!playerInfo->isLiveStream){
            cond1 = isQueueFull(playerInfo->audioStream, &playerInfo->audioPacketQueue) &&
                             isQueueFull(playerInfo->videoStream, &playerInfo->videoPacketQueue) &&
                             isQueueFull(playerInfo->textStream, &playerInfo->textPacketQueue);
            cond2= playerInfo->audioPacketQueue.size + playerInfo->videoPacketQueue.size + playerInfo->textPacketQueue.size > MAX_QUEUE_SIZE;
            if (cond1 || cond2 )
            {
                m_waitMutex.lock();
                m_waitCond.wait(&m_waitMutex,10);
                m_waitMutex.unlock();
                continue;
            }
        }

        //播放结束
        cond1=(!playerInfo->audioStream || (playerInfo->audioCodecEof && frame_queue_nb_remaining(&playerInfo->audioFrameQueue) == 0));
        cond2=(!playerInfo->videoStream || (playerInfo->videoCodecEof && frame_queue_nb_remaining(&playerInfo->videoFrameQueue) == 0));
        if (!this->m_reqPause && cond1 && cond2)
        {                                   
            emit eof();

            if(!playerInfo->isLiveStream && playerConfig->loop){
                //循环播放媒体文件
                //如果是直播流出现eof，则属于异常情况，应该退出线程
                seek(0);
            }else{
                //结束退出
                break;
            }
        }

        //解包
        m_lastReadDateTime=QDateTime::currentDateTime();
        retCode=av_read_frame(playerInfo->formatContext, pkt);
        if (retCode<0) {
            if ((retCode== AVERROR_EOF || avio_feof(playerInfo->formatContext->pb)) && !playerInfo->demuxEof) {
                if (playerInfo->videoStream)
                    packet_queue_put_nullpacket(&playerInfo->videoPacketQueue, playerInfo->videoStream->index);
                if (playerInfo->audioStream)
                    packet_queue_put_nullpacket(&playerInfo->audioPacketQueue, playerInfo->audioStream->index);
                if (playerInfo->textStream)
                    packet_queue_put_nullpacket(&playerInfo->textPacketQueue, playerInfo->textStream->index);
                playerInfo->demuxEof = true;
            }

            if (playerInfo->formatContext->pb && playerInfo->formatContext->pb->error!=0){
                emit error(QString("解包出现错误（%1）").arg(avErrorCodeToString(playerInfo->formatContext->pb->error)));
                break;
            }

            m_waitMutex.lock();
            m_waitCond.wait(&m_waitMutex,10);
            m_waitMutex.unlock();
            continue;
        } else {
            playerInfo->demuxEof = false;
        }

        //缓存
        if(playerInfo->audioStream && pkt->stream_index == playerInfo->audioStream->index ){
            packet_queue_put(&playerInfo->audioPacketQueue, pkt);
        }else if(playerInfo->videoStream && pkt->stream_index == playerInfo->videoStream->index
                 && !(playerInfo->videoStream->disposition & AV_DISPOSITION_ATTACHED_PIC)){
            //有时音频文件例如MP3也包含一个视频流，该流通常仅有一帧，即封面帧。
            //如果视频流是以封面帧的形式存在的，则不对其进行缓存
            packet_queue_put(&playerInfo->videoPacketQueue, pkt);
        }else if(playerInfo->textStream && pkt->stream_index == playerInfo->textStream->index ){
            packet_queue_put(&playerInfo->textPacketQueue, pkt);
        }else{
            av_packet_unref(pkt);
        }
    }

    //关闭流
    closeStream();

    //关闭缓冲
    stopPacketQueue();
}

/*
    打开流

    注意：如果相应流不存在或被禁用，则PlayerInfo对应stream为NULL
*/
bool DemuxThread::openStream()
{
    PlayerInfo* playerInfo=&m_player->m_playerInfo;
    PlayerConfig* playerConfig=&m_player->m_playerConfig;
    MediaInfo* mediaInfo=&m_player->m_mediaInfo;

    //解复用参数
    AVDictionary* demuxOptions=NULL;   
    if(playerConfig->rtspWithTcp){
        //RTSP拉流强制使用TCP传输
        av_dict_set(&demuxOptions, "rtsp_transport","tcp", 0);
    }
    if(playerConfig->timeout>0){
        //拉流超时时间（us）
        av_dict_set(&demuxOptions, "timeout",QString::number(playerConfig->timeout*1000).toStdString().data(), 0);
        //分流超时时间（s）
        av_dict_set(&demuxOptions, "max_analyze_duration",QString::number(playerConfig->timeout/1000).toStdString().data(),0);
    }else{
        av_dict_set(&demuxOptions, "timeout","5000000", 0);
        av_dict_set(&demuxOptions, "max_analyze_duration","5000000",0);
    }
    if(playerConfig->maxProbeSize>0){
        //分流时最多读取数据量(kb)
        av_dict_set(&demuxOptions, "probesize",QString::number(playerConfig->maxProbeSize).toStdString().data(),0);
    }
    av_dict_set(&demuxOptions, "scan_all_pmts", "1", AV_DICT_DONT_OVERWRITE);

    //打开流
    AVFormatContext*formatContext = avformat_alloc_context();
    if (!formatContext) {
        av_dict_free(&demuxOptions);
        m_lastErrorMsg="avformat_alloc_context失败";
        return false;
    }    
    formatContext->interrupt_callback.callback = DemuxThread::demuxInterruptCallback;
    formatContext->interrupt_callback.opaque = this;

    m_streamOpenDateTime=QDateTime::currentDateTime();
    int err = avformat_open_input(&formatContext, mediaInfo->url.toStdString().data(), NULL, &demuxOptions);
    av_dict_free(&demuxOptions);
    if (err < 0) {
        avformat_close_input(&formatContext);
        m_lastErrorMsg= QString("码流打开失败（%1）").arg(avErrorCodeToString(err));
        return false;
    }
    m_streamOpenDateTime=QDateTime();   //拉流成功后置空，不再检测拉流超时

    if (/*genpts*/ 0)//TODO
        formatContext->flags |= AVFMT_FLAG_GENPTS;
    if (formatContext->pb)
        formatContext->pb->eof_reached = 0;

    //分析流
    AVStream* videoStream=NULL,*audioStream=NULL,*textStream=NULL;
    int videoStreamIndex=-1,audioStreamIndex=-1,textStreamIndex=-1;
    if (playerConfig->videoEnable){
        videoStreamIndex=av_find_best_stream(formatContext, AVMEDIA_TYPE_VIDEO,-1, -1, NULL, 0);
    }
    if (playerConfig->audioEnable){
        audioStreamIndex =av_find_best_stream(formatContext, AVMEDIA_TYPE_AUDIO,-1,videoStreamIndex,NULL, 0);
    }
    if (playerConfig->videoEnable && playerConfig->audioEnable){
        textStreamIndex=av_find_best_stream(formatContext, AVMEDIA_TYPE_SUBTITLE,-1,
                                            (audioStreamIndex >= 0 ?audioStreamIndex :videoStreamIndex), NULL, 0);
    }

    if(videoStreamIndex>=0){
        videoStream=formatContext->streams[videoStreamIndex];
        videoStream->discard=AVDISCARD_DEFAULT;
    }
    if(audioStreamIndex>=0){
        audioStream=formatContext->streams[audioStreamIndex];
        audioStream->discard=AVDISCARD_DEFAULT;
    }
    if(textStreamIndex>=0){
        textStream=formatContext->streams[textStreamIndex];
        textStream->discard=AVDISCARD_DEFAULT;
    }
    if(videoStreamIndex<0 && audioStreamIndex<0 && textStreamIndex<0){
        avformat_close_input(&formatContext);
        m_lastErrorMsg="未解析出有效的媒体流";
        return false;
    }

    mediaInfo->containerFormat=QString(formatContext->iformat->long_name);
    mediaInfo->duration=formatContext->duration/1000;
    if(videoStream){
        mediaInfo->videoFormat=QString(avcodec_get_name(videoStream->codecpar->codec_id));
        mediaInfo->videoBitrate=qRound(videoStream->codecpar->bit_rate/(double)1024);
        mediaInfo->videoFramerate=av_q2d(videoStream->avg_frame_rate);
        mediaInfo->videoFrameWidth=videoStream->codecpar->width;
        mediaInfo->videoFrameHeight=videoStream->codecpar->height;
    }
    if(audioStream){
        mediaInfo->audioFormat=QString(avcodec_get_name(audioStream->codecpar->codec_id));
        mediaInfo->audioBitrate=qRound(audioStream->codecpar->bit_rate/(double)1024);
        mediaInfo->audioChannels=audioStream->codecpar->channels;
        mediaInfo->audioSamplerate=audioStream->codecpar->sample_rate;
    }

    playerInfo->formatContext=formatContext;
    playerInfo->videoStream=videoStream;
    playerInfo->audioStream=audioStream;
    playerInfo->textStream=textStream;
    playerInfo->maxFrameDuration = (formatContext->iformat->flags & AVFMT_TS_DISCONT) ? 10.0 : 3600.0;
    playerInfo->isLiveStream = isLiveStreamUrl(mediaInfo->url);
    if(playerConfig->avType==AV_SYNC_AUDIO_MASTER){
        if(audioStream){
            playerInfo->syncType=AV_SYNC_AUDIO_MASTER;
            playerInfo->masterClock=&playerInfo->audioClock;
        }else{
            playerInfo->syncType=AV_SYNC_EXTERNAL_CLOCK;
            playerInfo->masterClock=&playerInfo->externalClock;
        }
    }else if(playerConfig->avType==AV_SYNC_VIDEO_MASTER){
        if(videoStream){
            playerInfo->syncType=AV_SYNC_VIDEO_MASTER;
            playerInfo->masterClock=&playerInfo->videoClock;
        }else{
            playerInfo->syncType=AV_SYNC_AUDIO_MASTER;
            playerInfo->masterClock=&playerInfo->audioClock;
        }
    }else{
        playerInfo->syncType=AV_SYNC_EXTERNAL_CLOCK;
        playerInfo->masterClock=&playerInfo->externalClock;
    }

    return true;
}

/*
    关闭流
*/
void DemuxThread::closeStream()
{
    PlayerInfo* playerInfo=&m_player->m_playerInfo;

    if(playerInfo->formatContext){
        avformat_close_input(&playerInfo->formatContext);

        playerInfo->formatContext=NULL;
        playerInfo->videoStream=NULL;
        playerInfo->audioStream=NULL;
        playerInfo->textStream=NULL;
    }
}

/*
    启动缓冲队列
*/
void DemuxThread::startPacketQueue()
{
    PlayerInfo* playerInfo=&m_player->m_playerInfo;

    if(playerInfo->videoStream){
        packet_queue_start(&playerInfo->videoPacketQueue,&playerInfo->flushPkt);
    }
    if(playerInfo->audioStream){
        packet_queue_start(&playerInfo->audioPacketQueue,&playerInfo->flushPkt);
    }
    if(playerInfo->textStream){
        packet_queue_start(&playerInfo->textPacketQueue,&playerInfo->flushPkt);
    }
}

void DemuxThread::stopPacketQueue()
{
    PlayerInfo* playerInfo=&m_player->m_playerInfo;

    packet_queue_abort(&playerInfo->videoPacketQueue);
    frame_queue_signal(&playerInfo->videoFrameQueue);

    packet_queue_abort(&playerInfo->audioPacketQueue);
    frame_queue_signal(&playerInfo->audioFrameQueue);

    packet_queue_abort(&playerInfo->textPacketQueue);
    frame_queue_signal(&playerInfo->textFrameQueue);

}

bool DemuxThread::isQueueFull(AVStream *stream, PacketQueue *queue)
{

    return  !stream ||
            (stream->disposition & AV_DISPOSITION_ATTACHED_PIC) ||
            queue->abort_request ||
            (queue->nb_packets > MIN_FRAMES && (!queue->duration || av_q2d(stream->time_base) * queue->duration > 1.0));
}

bool DemuxThread::isLiveStreamUrl(const QString &url)
{
    QUrl u = QUrl::fromUserInput(url);

    return !url.isEmpty() && !u.isLocalFile();
}

/*
    向视频压缩帧缓冲队列压入封面帧
*/
bool DemuxThread::enqueueVideoAttachFrame()
{
    PlayerInfo* playerInfo=&m_player->m_playerInfo;
    if (playerInfo->videoStream && playerInfo->videoStream->disposition & AV_DISPOSITION_ATTACHED_PIC && playerInfo->videoStream->attached_pic.size>0) {
        AVPacket copy;
        if (av_packet_ref(&copy, &playerInfo->videoStream->attached_pic) == 0){
            packet_queue_put(&playerInfo->videoPacketQueue, &copy);
            packet_queue_put_nullpacket(&playerInfo->videoPacketQueue, playerInfo->videoStream->index);

            return true;
        }
    }

    return false;
}

/*
    avformat_open_input以及av_read_frame的回调函数，当此函数返回1时将停止打开流。
    当打开流成功时，此函数将不再调用。

    此函数目前用于判断打开流超时
*/
int DemuxThread::demuxInterruptCallback(void *ctx)
{
    DemuxThread *pThis = (DemuxThread *)ctx;
    PlayerConfig* playerConfig=&pThis->m_player->m_playerConfig;

    if(pThis->m_reqStop){
        return 1;
    }

    QDateTime curDateTime = QDateTime::currentDateTime();

    if(pThis->m_streamOpenDateTime.isValid()){  //拉流超时检测
        int interval = pThis->m_streamOpenDateTime.msecsTo(curDateTime);
        if(interval<0){
            //时钟向后发生跳变，则重新记录时间
            pThis->m_streamOpenDateTime = curDateTime;
            return 0;
        }else if(interval >= playerConfig->timeout){
            //超时
            return 1;
        }else{
            return 0;
        }
    }else if(pThis->m_lastReadDateTime.isValid()){  //读包超时检测
        int interval = pThis->m_lastReadDateTime.msecsTo(curDateTime);
        if(interval<0){
            //时钟向后发生跳变，则重新记录时间
            pThis->m_lastReadDateTime = curDateTime;
            return 0;
        }else if(interval >= playerConfig->timeout){
            //超时
            return 1;
        }else{
            return 0;
        }
    }else{
        return 0;
    }
}
